package code.processor;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import code.domain.ClassField;
import code.domain.ClassModel;
import code.domain.CodeFile;
import code.domain.Column;
import code.domain.Structure;
import code.domain.Table;
import code.domain.XMLBean;
import code.util.DBUtils;
import code.util.EnvConstants;
import code.util.FileUtils;
import code.util.FreemarkerConfiguration;
import code.util.XMLUtils;
import freemarker.core.Environment;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
 * 文件生成处理
 * @author puxin.zhang
 *
 */
public class Processor {
	
	private static final String REGEXP = "\\$\\{\\w*(\\.{1}\\w*)*\\}";
	
	/**
	 * 根据模板生成文件处理
	 * @throws TemplateException 
	 * @throws IOException 
	 */
	public void process() throws IOException, TemplateException{
		int t = 1;
		System.out.println(t++ + "-代码生成开始");
		//获得项目路径，绝对路径
		String projectDirPath = System.getProperty("user.dir");
		
		System.out.println(t++ + "-获取代码生成目标结构");
		List<XMLBean> beans = XMLUtils.getBeans();
		System.out.println(JSONArray.fromObject(beans));
		
		for (XMLBean bean : beans) {
			System.out.println(t++ + "开始生成 "+bean.getTableName()+" 表对应的代码");
			JSONObject jsonData = new JSONObject();
			
			JSONObject jsonBean = JSONObject.fromObject(bean);
			jsonData.put("bean", jsonBean);
			
			//将表和字段对象转换为Class对象
			System.out.println("\t将表和字段对象转换为Class对象");
			ClassModel model = transTable2Model(bean);
			JSONObject jsonModel = JSONObject.fromObject(model);
			jsonData.put("model", jsonModel);
			
			//获得要生成的代码的目录结构ID和根目录
			Map<String, String> codePathMap = bean.getCodePath();
			//遍历bean指定要生成的目录结构
			Set<String> structureIdSet = codePathMap.keySet();
			for (String structureId : structureIdSet) {
				//获得生成代码的目录结构ID和根目录
				String rootDir = resolveExpress(codePathMap.get(structureId), jsonData);
//				rootDir = rootDir.replaceAll(regex, replacement)
				if(!rootDir.startsWith("\\") && !rootDir.startsWith("/")){
					rootDir = File.separator + rootDir;
				}
				//根据bean配置的目录结构ID获得生成的目录结构
				Structure structure = XMLUtils.getStructure(structureId);
				structure.setId(structureId);
				structure.setPath(rootDir);	
				structure.setAbsolutePath(projectDirPath + rootDir);
				//使用数据模型和目录结构生成代码目录和文件
				System.out.println("\t使用"+structureId+"数据模型和目录结构生成代码目录和文件");
				generateStructureAndFile(jsonData, structure, true);
				
				//生成子表数据对象文件
				List<XMLBean> subBeans = bean.getSubBeans();
				for (XMLBean subBean : subBeans) {
					ClassModel subModel = null;
					List<ClassModel> childModels = model.getChildModels();
					models:for (ClassModel childModel : childModels) {
						if(StringUtils.equalsIgnoreCase(subBean.getModelName(), childModel.getName())){
							subModel = childModel;
							break models;
						}
					}
					JSONObject jsonSubData = new JSONObject();
					
					JSONObject jsonSubBean = JSONObject.fromObject(subBean);
					jsonSubData.put("bean", jsonSubBean);
					
					//将表和字段对象转换为Class对象
					JSONObject jsonSubModel = JSONObject.fromObject(subModel);
					jsonSubData.put("model", jsonSubModel);
					generateStructureAndFile(jsonSubData, structure, false);
				}
			}
		}
		System.out.println(t++ + "代码生成结束。");
	}
	/**
	 * 根据传入的数据模型和目录结构，生成代码目录和文件
	 * @param data
	 * @param structure
	 * @param isMerge 是否生成带合并属性文件
	 * @throws IOException 模板不存在
	 * @throws TemplateException 读取模板，生成文件出现异常
	 */
	private void generateStructureAndFile(JSONObject data, Structure dir, boolean executeMerge) throws IOException, TemplateException{
		JSONObject jsonBean = JSONObject.fromObject(data.get("bean"));
		boolean isReplace = StringUtils.equalsIgnoreCase(jsonBean.getString("isReplace"), "true");
		//生成当前目录
		FileUtils.mkDirs(dir.getAbsolutePath());
		//生成文件
		List<CodeFile> files = dir.getFiles();
		for (CodeFile file : files) {
			if(!file.isMerge() || executeMerge){
				//1、非合并文件，任何条件都需要处理， 或者是 2、有合并标记的文件，并且当前处理是执行合并，处理文件
				String fileName = file.getNamerules();
				fileName = resolveExpress(fileName, data);
				String filePath = dir.getAbsolutePath() + File.separator + fileName;
				filePath = filePath.replace("\\", File.separator).replace("/", File.separator);
				File codeFile = new File(filePath);
				
				FileUtils.mkDirs(filePath.substring(0, filePath.lastIndexOf(File.separator)));
				
				if(!isReplace && codeFile.exists()){
					throw new IOException("the source file " + codeFile.getPath() + " has exist. can not be replaced.");
				}
				
				String templatePath = file.getTemplate();
				templatePath = dir.getId() + File.separator + resolveExpress(templatePath, data);
				Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(codeFile), EnvConstants.ENCODING));
				
//	            //使用模板生成文件
				Template template = FreemarkerConfiguration.getConfigurationInstance().getTemplate(templatePath);
//				template.setEncoding(EnvConstants.ENCODING);
//	            template.process(data, out);
				Environment env = template.createProcessingEnvironment(data, out);
				env.setOutputEncoding(EnvConstants.ENCODING);
				env.process();
	            out.flush();
	            out.close();
			}
		}
		//生成子目录
		List<Structure> childDirs = dir.getChildDirs();
		if(childDirs!=null && !childDirs.isEmpty()){
			for (Structure childDir : childDirs) {
				childDir.setId(dir.getId());
				childDir.setAbsolutePath(dir.getAbsolutePath() + File.separator + childDir.getName());
				generateStructureAndFile(data, childDir, executeMerge);
			}
		}
	}
	/**
	 * 将数据库表对象转换为Class对象
	 * @param table
	 * @param config
	 * @return ClassModel 生成代码的Class对象
	 */
	private ClassModel transTable2Model(XMLBean config){
		
		//获得数据库 表和字段对象
		Table table = DBUtils.getTable(config.getTableName());
				
		ClassModel model = new ClassModel();
		model.setName(config.getModelName());
		model.setDbName(config.getTableName());
		model.setDesc(table.getDesc().replaceAll("[\r\n]+", " "));
		
		String initLowerName = config.getModelName().substring(0, 1).toLowerCase() + config.getModelName().substring(1);
		String initUpperName = config.getModelName().substring(0, 1).toUpperCase() + config.getModelName().substring(1);
		model.setInitLowerName(initLowerName);
		model.setInitUpperName(initUpperName);
		model.setLowerName(config.getModelName().toLowerCase());
		
		JSONObject jsonData = new JSONObject();		
		jsonData.put("model", JSONObject.fromObject(model));
		
		model.setPackageName(resolveExpress(config.getPackageName(), jsonData));
		
		JSONObject data = new JSONObject();
		data.put("model", JSONObject.fromObject(model));
		
		
		List<ClassField> fields = new ArrayList<ClassField>();		
		List<Column> cols = table.getColumnList();
		for (Column column : cols) {
			ClassField f = new ClassField();
			//处理数据库字段到类字段，目前规则是数据库字段，应以'_'符号分词，生成类字段时，首节字母小写，后续分词首字母大写，其他小写
			//如果数据库字段没有使用符号分词，则全部小写
			String columnName = column.getName().toLowerCase();
			String[] columnNameParts = columnName.split("_");
			columnName = "";
			for (int i = 0; i < columnNameParts.length; i++) {
				if(i > 0){
					columnName += (columnNameParts[i].substring(0, 1).toUpperCase() + columnNameParts[i].substring(1));
				}
				else{
					columnName += columnNameParts[i];
				}
			}
			f.setName(columnName);
			f.setDesc(column.getDesc().replaceAll("[\r\n]+", " "));
			f.setType(column.getType());
			f.setValue(column.getDefaultValue());
			f.setDbName(column.getName());
			fields.add(f);
		}
		model.setFields(fields);
		
		List<XMLBean> subBeans = config.getSubBeans();
		List<ClassModel> childModels = new ArrayList<ClassModel>();
		if(subBeans!=null && !subBeans.isEmpty()){
			for (XMLBean subBean : subBeans) {
				ClassModel childModel = transTable2Model(subBean);
				childModels.add(childModel);
			}
		}
		model.setChildModels(childModels);
		
		return model;
	}
	/**
	 * 解析带表达式${}的字符串，并返回解析后，已替换为实际内容的字符串
	 * 目前功能只能从bean里取值，没有运算功能
	 * @param express
	 * @param data
	 * @return
	 */
	private String resolveExpress(String express, JSONObject data){
		
		Pattern pattern = Pattern.compile(REGEXP);
		Matcher match = pattern.matcher(express);
		while (match.find()) {
			Object obj = null;
			JSONObject subData = JSONObject.fromObject(data);
			String matchExp = match.group();
			String matchExpText = matchExp.substring(2, matchExp.length()-1);
			String[] attrs = matchExpText.split("\\.");
			for (int i=0; i<attrs.length; i++) {
				obj = subData.get(attrs[i]);
				if((i+1)<attrs.length){
					subData = JSONObject.fromObject(obj);
				}
			}
			express = express.replace(matchExp, String.valueOf(obj));
		}
		return express;
	}
	
	public static void main(String[] args) {
		Processor processor = new Processor();
		try {
			processor.process();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TemplateException e) {
			e.printStackTrace();
		}
	}
}
